﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Narwhunderful.Aphelia.Framework;

namespace Narwhunderful.Aphelia.Framework.Input.Keyboard
{

    public class KeyboardManager : UpdatableComponent, IKeyboardComponent
    {

        Dictionary<Keys, int> _keys = new Dictionary<Keys,int>();
        List<Keys> _currentKeys = new List<Keys>();
        private const int RepeatDelay = 500;
        private const int RepeatRate = 25;

        KeyEventArgs _args = new KeyEventArgs(Keys.None, new KeyboardState(), new KeyboardState());

        KeyboardState _currentKeyboardState = new KeyboardState();
        public KeyboardState CurrentKeyboardState
        {
            get
            {
                return _currentKeyboardState;
            }
        }

        KeyboardState _lastKeyboardState = new KeyboardState();
        public KeyboardState LastKeyboardState
        {
            get
            {
                return _lastKeyboardState;
            }
        }
            
        public KeyboardManager(Game game)
            : base(game)
        {
            _currentKeyboardState = Microsoft.Xna.Framework.Input.Keyboard.GetState();
            _lastKeyboardState = Microsoft.Xna.Framework.Input.Keyboard.GetState();
            base.AlwaysNeedsUpdate = true;
        }

        #region IUpdatableComponent


        protected override void InternalUpdate(GameTime gameTime)
        {

            _lastKeyboardState = _currentKeyboardState;
            _currentKeyboardState = Microsoft.Xna.Framework.Input.Keyboard.GetState();

            _args.Alt = false;
            _args.CapsLock = false;
            _args.Control = false;
            _args.Shift = false;
            _args.CurrentState = _currentKeyboardState;
            _args.LastState = _lastKeyboardState;

            UpdateDefaultKeys(gameTime);
            UpdateKeys(gameTime);
        }

        protected virtual void UpdateDefaultKeys(GameTime gameTime)
        {
            foreach (Keys k in _currentKeyboardState.GetPressedKeys())
            {
                switch (k)
                {
                    case Keys.CapsLock:
                        _args.CapsLock = true;
                        break;

                    case Keys.LeftAlt:
                    case Keys.RightAlt:
                        _args.Alt = true;
                        break;

                    case Keys.LeftShift:
                    case Keys.RightShift:
                        _args.Shift = true;
                        break;

                    case Keys.LeftControl:
                    case Keys.RightControl:
                        _args.Control = true;
                        break;

                    default:
                        continue;
                }
            }
        }

        protected virtual void UpdateKeys(GameTime gameTime)
        {
            for (int x = 0; x < _currentKeys.Count; x++)
            {
                Keys k = _currentKeys[x];
                _args.Keys = k;

                if (_currentKeyboardState.IsKeyDown(k))
                {
                    _keys[k] -= gameTime.ElapsedGameTime.Milliseconds;
                    if (_keys[k] < 0) _keys[k] = 0;
                    if (_lastKeyboardState.IsKeyUp(k))
                    {
                        OnKeyPress(_args);
                        OnKeyDown(_args);
                    }
                }

                if (_currentKeyboardState.IsKeyUp(k))
                {
                    if (_lastKeyboardState.IsKeyDown(k))
                    {
                        _keys[k] = RepeatDelay;
                        OnKeyUp(_args);
                    }
                }

                if (_keys[k] == 0)
                {
                    _keys[k] = RepeatRate;
                    OnKeyDown(_args);
                }
            }
        }

        #endregion

        #region IKeyboardComponent

        public event EventHandler<KeyEventArgs> KeyDown = null;
        public event EventHandler<KeyEventArgs> KeyPress = null;
        public event EventHandler<KeyEventArgs> KeyUp = null;

        protected internal virtual void OnKeyDown(KeyEventArgs e)
        {
            KeyDown.SafeInvoke(this, e);
        }

        protected internal virtual void OnKeyPress(KeyEventArgs e)
        {
            KeyPress.SafeInvoke(this, e);
        }

        protected internal virtual void OnKeyUp(KeyEventArgs e)
        {
            KeyUp.SafeInvoke(this, e);
        }

        #endregion

        public void AddKey(Keys key)
        {
            switch (key)
            {
                case Keys.CapsLock:
                case Keys.LeftAlt:
                case Keys.RightAlt:
                case Keys.LeftShift:
                case Keys.RightShift:
                case Keys.LeftControl:
                case Keys.RightControl:
                    return;
            }
            if (_currentKeys.Contains(key)) return;
            _keys.Add(key, RepeatDelay);
            _currentKeys.Add(key);
        }

        public void AddKeys(IEnumerable<Keys> keys)
        {
            foreach (Keys k in keys)
            {
                AddKey(k);
            }
        }

        public void RemoveKey(Keys key)
        {
            switch (key)
            {
                case Keys.CapsLock:
                case Keys.LeftAlt:
                case Keys.RightAlt:
                case Keys.LeftShift:
                case Keys.RightShift:
                case Keys.LeftControl:
                case Keys.RightControl:
                    return;
            }
            _keys.Remove(key);
            _currentKeys.Remove(key);
        }

        public void RemoveKeys(IEnumerable<Keys> keys)
        {
            foreach (Keys k in keys)
            {
                RemoveKey(k);
            }
        }

        public static string KeyToString(KeyEventArgs e)
        {
            switch (e.Keys)
            {
                case Keys.A:
                    return (e.Shift || e.CapsLock) ? "A" : "a";
                case Keys.B:
                    return (e.Shift || e.CapsLock) ? "B" : "b";
                case Keys.C:
                    return (e.Shift || e.CapsLock) ? "C" : "c";
                case Keys.D:
                    return (e.Shift || e.CapsLock) ? "D" : "d";
                case Keys.E:
                    return (e.Shift || e.CapsLock) ? "E" : "e";
                case Keys.F:
                    return (e.Shift || e.CapsLock) ? "F" : "f";
                case Keys.G:
                    return (e.Shift || e.CapsLock) ? "G" : "g";
                case Keys.H:
                    return (e.Shift || e.CapsLock) ? "H" : "h";
                case Keys.I:
                    return (e.Shift || e.CapsLock) ? "I" : "i";
                case Keys.J:
                    return (e.Shift || e.CapsLock) ? "J" : "j";
                case Keys.K:
                    return (e.Shift || e.CapsLock) ? "K" : "k";
                case Keys.L:
                    return (e.Shift || e.CapsLock) ? "L" : "l";
                case Keys.M:
                    return (e.Shift || e.CapsLock) ? "M" : "m";
                case Keys.N:
                    return (e.Shift || e.CapsLock) ? "N" : "n";
                case Keys.O:
                    return (e.Shift || e.CapsLock) ? "O" : "o";
                case Keys.P:
                    return (e.Shift || e.CapsLock) ? "P" : "p";
                case Keys.Q:
                    return (e.Shift || e.CapsLock) ? "Q" : "q";
                case Keys.R:
                    return (e.Shift || e.CapsLock) ? "R" : "r";
                case Keys.S:
                    return (e.Shift || e.CapsLock) ? "S" : "s";
                case Keys.T:
                    return (e.Shift || e.CapsLock) ? "T" : "t";
                case Keys.U:
                    return (e.Shift || e.CapsLock) ? "U" : "u";
                case Keys.V:
                    return (e.Shift || e.CapsLock) ? "V" : "v";
                case Keys.W:
                    return (e.Shift || e.CapsLock) ? "W" : "w";
                case Keys.X:
                    return (e.Shift || e.CapsLock) ? "X" : "x";
                case Keys.Y:
                    return (e.Shift || e.CapsLock) ? "Y" : "y";
                case Keys.Z:
                    return (e.Shift || e.CapsLock) ? "Z" : "z";
                case Keys.D0:
                    return (e.Shift || e.CapsLock) ? ")" : "0";
                case Keys.D1:
                    return (e.Shift || e.CapsLock) ? "!" : "1";
                case Keys.D2:
                    return (e.Shift || e.CapsLock) ? "@" : "2";
                case Keys.D3:
                    return (e.Shift || e.CapsLock) ? "#" : "3";
                case Keys.D4:
                    return (e.Shift || e.CapsLock) ? "$" : "4";
                case Keys.D5:
                    return (e.Shift || e.CapsLock) ? "%" : "5";
                case Keys.D6:
                    return (e.Shift || e.CapsLock) ? "^" : "6";
                case Keys.D7:
                    return (e.Shift || e.CapsLock) ? "&" : "7";
                case Keys.D8:
                    return (e.Shift || e.CapsLock) ? "*" : "8";
                case Keys.D9:
                    return (e.Shift || e.CapsLock) ? "(" : "9";
                case Keys.OemPlus:
                    return (e.Shift || e.CapsLock) ? "+" : "=";
                case Keys.OemMinus:
                    return (e.Shift || e.CapsLock) ? "_" : "-";
                case Keys.OemOpenBrackets:
                    return (e.Shift || e.CapsLock) ? "{" : "[";
                case Keys.OemCloseBrackets:
                    return (e.Shift || e.CapsLock) ? "}" : "]";
                case Keys.OemQuestion:
                    return (e.Shift || e.CapsLock) ? "?" : "/";
                case Keys.OemPeriod:
                    return (e.Shift || e.CapsLock) ? ">" : ".";
                case Keys.OemComma:
                    return (e.Shift || e.CapsLock) ? "<" : ",";
                case Keys.OemPipe:
                    return (e.Shift || e.CapsLock) ? "|" : "\\";
                case Keys.Space:
                    return " ";
                case Keys.OemSemicolon:
                    return (e.Shift || e.CapsLock) ? ":" : ";";
                case Keys.OemQuotes:
                    return (e.Shift || e.CapsLock) ? "\"" : "'";
                case Keys.OemTilde:
                    return (e.Shift || e.CapsLock) ? "~" : "`";
                default:
                    return "";
            }
        }

    }

}
